package net.sf.nlp.ltag.data;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.sf.nlp.ltag.util.LTAG;


public class LTAGTree implements Cloneable 
{
	public enum TreeType {A, I};
	public enum NodeLocation {LEFT, RIGHT, EQUALS};
	
	private Long id;
	private TreeType type = TreeType.I;
	private String description;
	private int wordIndex;
	
	private LTAGNode root;
	private LTAGNode anchor;
	private LTAGNode foot;
	
	private int footNodeCount= 0;
	private int anchorNodeCount = 0;
	private int nodesCount = 0;
	private int leafNodeCount = 0;
	
	private long count = 0;
	private long beginCount = 0;
	
	private List<LTAGNode> leafNodes = null;
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public TreeType getType() {
		return type;
	}
	public LTAGNode getRoot() {
		return root;
	}
	public void setRoot(LTAGNode root) {
		this.root = root;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	public int getWordIndex() {
		return wordIndex;
	}
	public void setWordIndex(int wordIndex) {
		this.wordIndex = wordIndex;
	}
	public int getLeafNodeCount() {
		return leafNodeCount;
	}
	public int getNodesCount() {
		return nodesCount;
	}
	public long getCount() {
		return count;
	}
	public void setCount(long count) {
		this.count = count;
	}
	public long getBeginCount() {
		return beginCount;
	}
	public void setBeginCount(long beginCount) {
		this.beginCount = beginCount;
	}
	public String getIdentifierKey()
	{
		StringBuffer key = new StringBuffer();
		
		key.append(id);
		
		return key.toString();
	}
	
	public void generateNodeLocation()
	{
		nodesCount = 0;
		
		generateNodeLocation(root);
	}
	
	private void generateNodeLocation(LTAGNode node)
	{
		node.setLocation(nodesCount++);
		
		if (node.getChildren() == null)
			return;
		
		for (LTAGNode child : node.getChildren())
			generateNodeLocation(child);
	}
	
	public void validate() throws Exception
	{
		footNodeCount= 0;
		anchorNodeCount = 0;
		nodesCount = 0;
		leafNodeCount = 0;
		
		validate(root);
	}
	
	private void validate(LTAGNode node) throws Exception
	{
		node.setLocation(nodesCount++);
		
		if ("#".equals(node.getType()))
			anchorNodeCount++;
		if ("*".equals(node.getType()))
		{
			type = TreeType.A;
			if (node.getNodeText().equals(root.getNodeText()))
				footNodeCount++;
			else
				throw new Exception("The foot node must equals to root node in adjunct tree!");
		}
		
		if (node.getChildren() == null || node.getChildren().size() == 0)
		{
			if (node.getType() == null)
				throw new Exception("The leave node have to set a type");
			
			node.setLeafIndex(leafNodeCount);
			leafNodeCount++;
			return;
		}
		
		if (node.getType() != null)
			throw new Exception("The node[" + node.getNodeText() + "] should not set type to " + node.getType());
		
		for (LTAGNode child : node.getChildren())
			validate(child);
		
		if (node.equals(root))
		{
			if (anchorNodeCount > 1)
				throw new Exception("The tree need only one anchor node!");
			
			if ("I".equals(type) && footNodeCount > 0)
				throw new Exception("The initial tree can not contain foot node!");
			else if ("A".equals(type) && footNodeCount > 1)
				throw new Exception("The adjunct tree need only one foot node!");
		}
	}
	
	public LTAGNode getNodeByPath(String path)
	{
		return getNodeByPath(root, path, root.getNodeText());
	}
	
	private LTAGNode getNodeByPath(LTAGNode node, String path, String temp)
	{
		if (path.equals(temp))
			return node;
		
		if (node.getChildren() != null && node.getChildren().size() > 0)
		{
			for (int i = 0; i < node.getChildren().size(); i++)
			{
				LTAGNode child = node.getChildren().get(i);
				String childPath = temp + "-" +child.getNodeText();
				
				LTAGNode ret = getNodeByPath(child, path, childPath);
				
				if (ret != null)
					return ret;
			}
		}
		
		return null;
		
	}
	
	public LTAGNode getAnchor()
	{
		if (anchor != null)
			return anchor;
		
		anchor = getAnchor(root);
		return anchor;
	}
	
	private LTAGNode getAnchor(LTAGNode node)
	{
		if ("#".equals(node.getType()))
			return node;
		
		List<LTAGNode> children = node.getChildren();
		if (children == null)
			return null;
		
		for (LTAGNode child : children)
		{
			LTAGNode t = getAnchor(child);
			if (t != null)
				return t;
		}
		
		return null;
	}

	public void setAnchor(LTAGNode anchor) {
		this.anchor = anchor;
	}
	
	public LTAGNode getFoot() {
		if (foot != null)
			return foot;
		
		foot = getFoot(root);
		return foot;
	}
	
	private LTAGNode getFoot(LTAGNode node)
	{
		if ("*".equals(node.getType()))
			return node;
		
		List<LTAGNode> children = node.getChildren();
		if (children == null)
			return null;
		
		for (LTAGNode child : children)
		{
			LTAGNode t = getFoot(child);
			if (t != null)
				return t;
		}
		
		return null;
	}
	
	public void setFoot(LTAGNode foot) {
		this.foot = foot;
	}
	public void appendWord(Word word)
	{
		LTAGNode node = getAnchor();
		node.setWord(word);
	}
	
	public void removeNodes(String removeNodeTexts, String supportPUs)
	{
		removeNodes(root, removeNodeTexts, supportPUs);
		
		if (isNeedRemoved(root, removeNodeTexts, supportPUs))
			root = null;
	}
	
	private void removeNodes(LTAGNode node, String removeNodeTexts, String supportPUs)
	{
		if (node.getChildren() == null)
			return;
		
		Iterator<LTAGNode> it = node.getChildren().iterator();
		
		while (it.hasNext())
		{
			LTAGNode child = it.next();
			
			removeNodes(child, removeNodeTexts, supportPUs);
			
			if (isNeedRemoved(child, removeNodeTexts, supportPUs))
				it.remove();
		}
	}
	
	private boolean isNeedRemoved(LTAGNode node, String removeNodeTexts, String supportPUs)
	{
		if (node.getWord() == null && (node.getChildren() == null || node.getChildren().size() == 0))
			return true;
		
		if (removeNodeTexts.contains(node.getNodeText() + LTAG.SEPERATOR) || (node.getWord() != null && removeNodeTexts.contains(node.getWord().getLexical() + LTAG.SEPERATOR)))
			return true;
		
		if ("PU".equals(node.getNodeText()) && !supportPUs.contains(node.getWord().getLexical() + LTAG.SEPERATOR))
			return true;
		
		return false;
	}
	
	public int distance4LeafNode(LTAGNode node1, LTAGNode node2)
	{
		
		return 0;
	}
	
	// 返回两个叶子节在树中的相对位置 (左边，右边，同一节点)
	public NodeLocation compareLocation(LTAGNode node1, LTAGNode node2)
	{
		if (node1 == node2)
			return NodeLocation.EQUALS;
		
		if (node1.getLocation() < node2.getLocation())
			return NodeLocation.LEFT;
		else
			return NodeLocation.RIGHT;
	}
	
	public String toString()
	{
		StringBuffer sb = new StringBuffer();
		
		sb.append("id:").append(id).append(" ");
		sb.append("type:").append(type).append(" ");
		sb.append("content:").append(root.toString());
		
		return sb.toString();
	}
	
	public String toLTAG()
	{
		return root.toLTAG();
	}
	
	public String toLTAGNoWord()
	{
		return root.toLTAGNoWord();
	}
	
	public String toSuperLTAG()
	{
		return root.toSuperLTAG();
	}

	public String toLeafString()
	{
		return toLeafString(root);
	}
	
	private String toLeafString(LTAGNode node)
	{
		StringBuffer sb = new StringBuffer();
		
		List<LTAGNode> children = node.getChildren();
		
		if (children == null || children.size() == 0)
		{
			if (node.getWord() != null)
				sb.append(node.getWord().getLexical());
		}
		else
		{
			for (LTAGNode child : children)
			{
				sb.append(toLeafString(child));
			}
		}
		
		return sb.toString();
	}
	
	public List<LTAGNode> getLeafNodes()
	{
		if (leafNodes != null)
			return leafNodes;
		
		leafNodes = new ArrayList<LTAGNode>();
		
		getLeafNodes(root);
		
		return leafNodes;
	}
	
	public void getLeafNodes(LTAGNode node)
	{
		List<LTAGNode> children = node.getChildren();
		
		if (children == null || children.size() == 0)
		{
			leafNodes.add(node);
		}
		else
		{
			for (LTAGNode child : children)
				getLeafNodes(child);
		}
	}
	
	public LTAGTree clone()
	{
		LTAGTree clone = null;
		
		try 
		{
			clone = (LTAGTree)super.clone();
			clone.setRoot(this.getRoot().clone());
			clone.setAnchor(null);
			clone.setFoot(null);
		} 
		catch (CloneNotSupportedException e) 
		{
			e.printStackTrace();
		}
		
		return clone;
	}
	
	public LTAGNode getNodeByLocation(int location)
	{
		return getNodeByLocation(root, location);
	}
	
	private LTAGNode getNodeByLocation(LTAGNode node, int location)
	{
		if (node.getLocation() == location)
			return node;
		
		if (node.getChildren() != null)
		{
			for (LTAGNode child : node.getChildren())
			{
				LTAGNode ret = getNodeByLocation(child, location);
				
				if (ret != null)
					return ret;
			}
		}
		
		return null;
	}
}
